/*
 * @Author: Fan Dong
 * @Date: 2021-07-01 15:46:23
 * @LastEditors: Fan Dong
 * @LastEditTime: 2021-07-08 15:01:21
 * @Description: 文件描述
 * @FilePath: \learn-typescript\ts\7.泛型.ts
 */

// 1、泛型的定义
/*

// 泛型：软件工程中，我们不仅要创建一致定义良好的API，同时也要考虑可重用性。组件不仅能够支持当前的数据类型，同时也能支持未来的数据类型，这在创建大型系统时候为你提供了十分灵活的功能。

// 在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。

// 通俗理解：泛型就是解决类、接口方法的复用性，以及对不特定数据类型的支持(类型效验)。
*/

/*
// 2、泛型函数

  
// 只能返回string类型的数据
// function getData(value:string):string {
//   return value;
// }

// 同时返回string和number类型  （代码冗余）
// function getData1(value:string):string {
//   return value;
// }
// function getData2(value:number):number {
//   return value;
// }

// 同时返回string和number类型  any可以解决这个问题
// function getData3(value: any): any {
//   return value;
// }
// getData3(123)
// getData3('str')

// any放弃了类型检查，传入什么 返回什么，比如传入number类型必须返回number类型 传入string类型必须返回string类型

// 出入的参数类型和返回的参数类型可以不一致
// function getData3(value: any): any {
//   return '加加减减';
// }

// 泛型：可以支持不特定的数据类型  要求：传入的参数和返回的参数一致
// <T> 可以是任意大写字母，但是3个字母必须保持一致,通常用T表示

function getData5<T>(value: T): T {
  return value;
}
// function getData6<A>(value:A):A {
//   return value;
// }

getData5<number>(123);
getData5<string>("123");
// getData5<number>('123') // 错误


function getData7<T>(value: T): any {
  return '456';
}

getData7<number>(123)
getData7<string>('这是一个泛型')
*/

// 3、泛型类

/*
// 最小值算法   只支持number类型
class MinClass {
  list: number[] = [];

  add(num: number) {
    this.list.push(num);
  }
  min() {
    let minNum = this.list[0];
    for (let i = 0; i < this.list.length; i++) {
      const element = this.list[i];
      if (minNum > element) {
        minNum = element;
      }
    }
    return minNum;
  }
}

let m = new MinClass();
m.add(12);
m.add(20);
m.add(21);

console.log(m.min())
*/
class MinClass<T> {
  list: T[] = [];
  add(value: T): void {
    this.list.push(value);
  }
  min(): T {
    let minNum = this.list[0];
    for (let i = 0; i < this.list.length; i++) {
      const element = this.list[i];
      // 字母判断大小使用阿斯克码
      if (minNum > element) {
        minNum = element;
      }
    }
    return minNum;
  }
}

let m1 = new MinClass<number>(); // 实例化 并且制定了类的T代表的类型是number
m1.add(123);
m1.add(20);
m1.add(25);

console.log(m1.min());

let m2 = new MinClass<string>();
m2.add("f");
m2.add("c");
m2.add("a");
m2.add("e");

console.log(m2.min());

// 4、泛型接口

/*
// 函数类型接口
interface ConfigFn {
  (value1: string, value2: string): string;
}

let setData: ConfigFn = function (name: string, value: string): string {
  return name + value;
};

console.log(setData("name", "张三"))
*/

// 第一种方式
interface ConfigFn {
  <T>(value: T): T;
}

let getData: ConfigFn = function <T>(value: T): T {
  return value;
};

getData<string>("张三");
// getData<string>(123); // 错误

// 第二种方式
interface ConfigFn2<T> {
  (value: T): T;
}

function getData2<T>(value: T): T {
  return value;
}

let myGetData: ConfigFn2<string> = getData2;

console.log(myGetData("框架计算机技术"));
// console.log(myGetData(10)) //  错误

/*
// 把类作为参数来约束传入的类型
class User {
  username: string | undefined;
  password: string | undefined;
}

class MyDb {
  add(user: User): boolean {
    console.log(user);
    return true;
  }
}

let u = new User();
u.username = "张三";
u.password = "123456";

let db = new MyDb();
db.add(u);

class ArticleCate {
  title: string | undefined;
  desc: string | undefined;
  status: number | undefined;
}

class MyDb2 {
  add(info: ArticleCate): boolean {
    console.log(info);
    return true;
  }
}

let aa1 = new ArticleCate();
aa1.title = "国内";
aa1.desc = "国内新闻";
aa1.status = 1;

let db2 = new MyDb2();
db2.add(aa1);
*/

// 把类作为参数出入泛型类
// 操作数据库的泛型类
class MyDb<T> {
  add(info: T): boolean {
    console.log(info);
    return true;
  }
}

// 想给user表增加数据
// 1、定义一个user类和数据库进行映射
class User {
  username: string | undefined;
  password: string | undefined;
}

let u = new User();
u.username = "张三";
u.password = "123456";

let db = new MyDb<User>();
db.add(u);

class ArticleCate {
  title: string | undefined;
  desc: string | undefined;
  status: number | undefined;

  constructor(params: {
    title: string | undefined;
    desc: string | undefined;
    status?: number | undefined;
  }) {
    this.title = params.title;
    this.desc = params.desc;
    this.status = params.status;
  }
}

let art = new ArticleCate({
  title: "分类",
  desc: "111111111111",
});

let db2 = new MyDb<ArticleCate>();
db2.add(art);
